/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.client.ui.aggregate;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sinai.mshab.client.ui.FormComponent;
import com.sinai.mshab.client.ui.basic.BasicFormComponent;
import com.sinai.mshab.client.ui.label.DescriptionLabel;
import com.sinai.mshab.client.util.Answer;

/**
 * The ConditionalComponent class allows for the developer to show and hide
 * specific widgets depending on the condition of one widget. The condition
 * widget, as it's called, can be any {@link BasicFormComponent}. After
 * construction a ConditionalComponent with a condition widget, additional
 * widgets can be added that will be shown depending on whether the condition
 * widget meets a specific String criteria. This is done using mshab's observer
 * framework.
 * 
 * @author Arthur Kalmenson
 */
public class ConditionalComponent extends AggregateFormComponent {

	/**
	 * Maps the String condition that needs to be met with the widget that will
	 * be shown.
	 */
	private Map conditionToWidget;

	/**
	 * The condition widget for this ConditionalComponent.
	 */
	private BasicFormComponent conditionWidget;

	/**
	 * Creates a new ConditionalComponent using <code>conditionWidget</code>
	 * as the conditional widget. ConditionalComponent will be oriented in a
	 * vertical fashion by default.
	 * 
	 * @param conditionWidget
	 *            the widget that will dictate when any other widgets appear.
	 */
	public ConditionalComponent(BasicFormComponent conditionWidget) {

		// initialize
		super.initializeWidget(new VerticalPanel());
		conditionToWidget = new HashMap();

		// add the condition widget to our panel.
		panel.add(conditionWidget);
		this.conditionWidget = conditionWidget;

		// add myself as an observer of our condition widget.
		conditionWidget.addObserver(this);

		initWidget(panel);
	}

	/**
	 * Adds <code>component</code> to this ConditionalComponent. When
	 * <code>condition</code> is met by ConditionalComponent's condition
	 * widget, we show <code>component</code>.
	 * 
	 * @param condition
	 *            the String condition the condition widget must meet in order
	 *            for <code>component</code> to be made visible.
	 * @param component
	 *            the component we want to hide until <code>condition</code>
	 *            is met.
	 */
	public void addWidget(String condition, FormComponent component) {

		conditionToWidget.put(condition, component);

		// if the widget shouldn't be visible, we'll make it invisible.
		if (!(getVisibleWidget() == component)) {
			component.setVisible(false);
		}

		// add the widget to the panel.
		panel.add(component);
	}

	public Object clone() {

		// copy the condition widget and create a new ConditionalComponent.
		BasicFormComponent copyCondition = (BasicFormComponent) conditionWidget
				.clone();
		ConditionalComponent result = new ConditionalComponent(copyCondition);

		// now copy over all the widgets.
		for (Iterator conditions = conditionToWidget.keySet().iterator(); conditions
				.hasNext();) {
			String condition = (String) conditions.next();
			FormComponent component = (FormComponent) conditionToWidget
					.get(condition);
			result.addWidget(condition, component);
		}

		return result;
	}

	public VerticalPanel toReport() {
		VerticalPanel report = new VerticalPanel();

		// add the description label
		report.add(new DescriptionLabel(description));

		// add the condition widget's report
		report.add(conditionWidget.toReport());

		// add the report of the visible widget depending on the type of widget
		// it is.
		FormComponent component = getVisibleWidget();
		if (component instanceof AggregateFormComponent) {
			AggregateFormComponent aggComponent = (AggregateFormComponent) component;
			VerticalPanel aggReport = aggComponent.toReport();
			if (aggReport != null) {
				report.add(aggReport);
			}
		} else if (component instanceof BasicFormComponent) {
			BasicFormComponent basicComponent = (BasicFormComponent) component;
			HorizontalPanel basicReport = basicComponent.toReport();
			if (basicReport != null) {
				report.add(basicReport);
			}
		}

		return report;
	}

	public void sendAnswer(DataReceiver receiver) {

		// send the Answer of the condition widget and of the widget that is
		// currently visible.
		conditionWidget.sendAnswer(receiver);

		if (getVisibleWidget() != null) {
			getVisibleWidget().sendAnswer(receiver);
		}
	}

	public boolean populate() {

		// populate my condition widget.
		boolean result = conditionWidget.populate();

		if (result) {

			// now get the visible widget and populate it.
			result = getVisibleWidget().populate();
		} else {

			// if the condition widget wasn't populated, we need to populate all
			// the other widgets and determine the state of the condition widget
			// from there.
			Iterator showConditions = conditionToWidget.keySet().iterator();
			while (showConditions.hasNext()) {
				String showCondition = (String) showConditions.next();
				FormComponent showWidget = (FormComponent) conditionToWidget
						.get(showCondition);
				if (showWidget.populate()) {
					conditionWidget.setAnswer(showCondition);
				}
			}
		}

		return result;
	}

	public boolean isEmpty() {
		return conditionToWidget.isEmpty() && conditionWidget == null;
	}

	public void receiveNotification(FormComponent who, FormComponent originator) {

		// if it's the conditionWidget then we grab it's answer and show the
		// appropriate widget.
		if (originator == conditionWidget) {
			String state = conditionWidget.getAnswer().getFirstAnswer();

			// make all the components invisible.
			setAllInvisible();

			// now make the widget that is suppose to be shown is visible (if
			// any widget is suppose to be show).
			FormComponent widget = (FormComponent) conditionToWidget.get(state);
			if (widget != null) {
				widget.setVisible(true);
			}
		}
	}

	/**
	 * Returns the current visible widget. This depends on the state of the
	 * condition widget. If no widgets are visible, null will be returned.
	 * 
	 * @return the current visible widget. This depends on the state of the
	 *         condition widget.
	 */
	public FormComponent getVisibleWidget() {
		String state = conditionWidget.getAnswer().getFirstAnswer();
		FormComponent visibleWidget = (FormComponent) conditionToWidget
				.get(state);

		// make the widget visible if it exists.
		if (visibleWidget != null) {
			visibleWidget.setVisible(true);
		}

		return visibleWidget;
	}

	/**
	 * Changes the visibility to false for all the widgets in this
	 * <code>ConditionalComponent</code>
	 */
	private void setAllInvisible() {
		for (Iterator conditions = conditionToWidget.keySet().iterator(); conditions
				.hasNext();) {
			String condition = (String) conditions.next();
			FormComponent component = (FormComponent) conditionToWidget
					.get(condition);
			component.setVisible(false);
		}
	}
}
